module bpu #(parameter ADDRESS_WIDTH = 32,
			 parameter TABLE_WIDTH = 4)
			(input isBranchID, 						//instruction in ID is a branch?
			 input isJumpID,
			 input [ADDRESS_WIDTH-1:0] instrAddrID, //address of instruction in ID
			 input [ADDRESS_WIDTH-1:0] branchAddrID, //target of instr in ID (if branch?)
			
			 input branchingEX,					    		//EX result indicates a branch!
			 input [ADDRESS_WIDTH-1:0] branchAddrIn,	//absolute destination address of instruction in EX
			 input branchPredictedEX,						//BPU predicted branch for instruction currently in EX?
			 input [ADDRESS_WIDTH-1:0] branchPredictionEX,	//target of BPU-branch for instruction currently in EX
			 input [ADDRESS_WIDTH-1:0] instrAddrEX, 	//address of instruction in EX
			
			 input [ADDRESS_WIDTH-1:0] PC,
			 input clk,
			
			 output reg [ADDRESS_WIDTH-1:0] branchAddrFetch, //address for fetch to branch to
			 output reg branchFetch, 	//fetch should branch to address?
			 output flushIFID, //flush IF/ID latch
			
			 output branchPredictedOut,
			 output [ADDRESS_WIDTH-1:0] branchPredictionOut
			);
	
	wire badGuessEX;
	wire [TABLE_WIDTH-1:0] BPTIndex;
	wire [ADDRESS_WIDTH-1:0] branchAddrBPT;
	wire [ADDRESS_WIDTH-1:0] BPTSource;
	wire [ADDRESS_WIDTH-1:0] BPTOut;
	wire branchPredictionLogic;
	wire branchPrediction;
	
	assign badGuessEX = (branchingEX ^ branchPredictedEX) |   //predicted branch and aren't, didn't predict branch and am
							  (branchingEX & branchPredictedEX & 	 //or predicted branch -
							   branchPredictionEX != branchAddrIn); //- to the wrong place!
	
	assign BPTIndex = instrAddrEX[TABLE_WIDTH-1:0]; //truncates implicitly? maybe not?
	assign branchAddrBPT = branchingEX ? branchAddrIn : BPTOut; //store branch target if branched; else, hold old value
	assign BPTSource = instrAddrID;
	
	assign branchPredictionLogic = branchAddrID < PC;
	assign branchPrediction = (isBranchID && branchPredictionLogic) || isJumpID;
	assign flushIFID = badGuessEX; //flush if problems in EX
	assign branchPredictedOut = branchPrediction; //value of branchPredicted for IDEX latch
	assign branchPredictionOut = branchAddrID;
	
	always @(badGuessEX,branchingEX,branchAddrIn,instrAddrEX,branchPrediction,BPTOut,PC)
		if (badGuessEX) begin	//branch to branch target, or back if branch not taken
			if (branchingEX)
				branchAddrFetch <= branchAddrIn; //to branch target
			else
				branchAddrFetch <= instrAddrEX + 2'b10; //back (branch not taken)
			branchFetch <= 1'b1;
		end else begin
			if (branchPrediction) begin //branch predicted! woo
				branchFetch <= 1'b1;
				branchAddrFetch <= BPTOut; //branch to addr in table
			end else begin
				branchFetch <= 1'b0;
				branchAddrFetch <= PC + 2'b10; //doesn't matter unless branchFetch signal is removed
			end
		end
	
	bpt #(.ADDRESS_WIDTH(ADDRESS_WIDTH),
		  .TABLE_WIDTH(TABLE_WIDTH))
		  BPT
		 (	// Inputs
				.clk(clk),
				.branchAddressID(BPTSource),
				.branchAddressEX(branchAddrBPT),
				.branchIndex(BPTIndex),
				
				// Outputs
				.branchPrediction(BPTOut)
			);
	
endmodule